/* s2 - Spectre mitigations
   Copyright (C) 2018 Ariadne Devos

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */

#ifndef _sHT_NOSPEC_H
#define _sHT_NOSPEC_H

/** Spectre mitigations

  Processors can *speculatively* ignore a branch or falsily take it,
  but will eventually correct incorrect decisions. If not accounted for
  by the programmer, this creates side-channel attacks based upon timing.

  @var{sHT_index_nospec} and @var{sHT_modulo_nospec} sanitise array indices
  and @var{sHT_despeculate} asks the processor to check decisions were
  correct. When using @var{sHT_despeculate}, often some information has to be
  tracked with @var{sHT_depend}.

  Website: https://spectreattack.com (2018).
  LWN: https://lwn.net/Kernel/Index (search for Spectre) (2018). */

#include <stddef.h>
#include <sys/types.h>
#include <limits.h>

#include <sHT/compiler.h>

/* Return pos, which is, ignoring speculation, less than length.
   Even in case of speculation, the return value is less than length. */
__attribute__((const))
static inline size_t
sHT_index_nospec(size_t pos, size_t length)
{
	sHT_hide_var(pos);

	/* https://lwn.net/Articles/744287/, from Linux */
	/* TODO: there are two x86-64 intruction for this */
	ssize_t mask = ~(ssize_t) (pos | (length - 1 - pos)) >> (CHAR_BIT * sizeof (ssize_t) - 1);
	return pos & (size_t) mask;
}

/** Compute @var{i} modulo @var{n}, even on a speculative execution.

  @var{i}: the dividend, an unsigned integer that fits in a @var{size_t}
  @var{n}: the divisor (a.k.a. modulus), an positive integer that fits in a
   @var{size_t}.

  The speculative values of @var{i} and @var{n} will be used.

  @var{i} modulo @var{n} is also the remainder of @var{i} / @var{n}. If you do
  not know what this means, consult a resource on arithmetic (e.g.
  <https://en.wikipedia.org/wiki/Modulo_operation>).

  If a compiler ignorant of speculation believes that @code{i < n},
  it might rewrite @code{i % n} to @code{i}. This may be problematic in
  some situations, however, and there this function comes in. */
__attribute__((const))
static inline size_t
sHT_modulo_nospec(size_t i, size_t n)
{
	/* On x86-64, '%' is computed with a division instruction. It is said
	   that it may set status flags, but these are marked as 'undefined',
	   so GCC cannot make false assumptions. Therefore, there is no need
	   for inline assembly.

	   TODO: bug #1: check other architectures.

	   Reference:
	   AMD64 Architecture Programmer's Manual, Volume 3:
	   General-Purpose and System Instructions (2018), at
	   https://support.amd.com/TechDocs/24594.pdf */
	sHT_hide_var(i);
	return i % n;
}

/** Stop the current execution if it speculated the value of @var{var} wrongly.
 Do this recursively for all values @var{var} is supposedly-dependant upon.

 (Supposed dependencies can be introduced with @var{sHT_depend}. I do not know
 if this concept exists microarchitecturally, but they may need to be tracked
 to stop the compiler from reordering loads and stores, which might interfere
 with our Spectre mitigations. But I am not a microarchitecture expert.

 --- Ariadne) */
#define sHT_despeculate(var) do { } while (0)
#undef sHT_despeculate

#if defined(__x86_64__) || defined(__i386__)
#define sHT_despeculate(var) \
	do { \
		/* This is what Intel tells us to do. It actually has stronger
		   semantics than the comment suggests. */ \
		__asm__("lfence" : "=r" (var) : "0" (var)); \
	} while (0)
#elif defined(__aarch64__) || defined(__arm__)
#define sHT_despeculate(var) \
	do { \
		/* ARM says: 'ISB+DSB SY' (2018).
		   https://patchwork.ozlabs.org/patch/941448 (2018) lets GCC
		   emit these instructions.
		   ARM also says 'CSDB', but qemu can't parse that, so I
		   assume it cannot used - yet.
		   It is said that this is a full speculation barrier. */ \
		__asm__("isb;dsb sy" : "=r" (var) : "0" (var)); \
	} while (0)
#endif

#endif
